import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.text.*;
import java.util.*;
import java.io.*;

public class MM extends MarkovModel {

  protected double[][] loge;

  protected static boolean debugTrain = true;

  public MM (Object[] esym, double[][] emat ) {
    for (int i=0; i<emat.length; i++) if (esym.length != emat[i].length) throw new IllegalArgumentException("MM: esym and emat disagree");
    this.esym = esym;
    nesym = esym.length;
    loge = new double[emat.length + 1][esym.length];
    for (int b=0; b < nesym; b++) {
      double fromstart = Math.log(1.0/esym.length);
      loge[0][b] = fromstart;
      for (int k=0; k < emat.length; k++) loge[k+1][b] = Math.log(emat[k][b]);
    }
  }

  public void print(PrintStream out) { 
    out.println("Symbol generation probabilities:");
    for (int b=0; b<nesym; b++) out.print(esym[b] + hdrpad);
    out.println();
    for (int i=1; i < loge.length; i++) {
      for (int b=0; b<nesym; b++) out.print(fmtlog(loge[i][b]));
      out.println();
    }
  }

  public static MM train(Object[][] xs, Object[] esym ) {
    int nseqs  = xs.length;
    int nesym  = esym.length;
    double[][] E = new double[nesym][nesym];
    double[][] emat = new double[nesym][nesym];
    for (int j=0; j<nesym ; j++) for (int k=0; k<nesym ; k++) E[j][k] = 0.0;
    MM mm = new MM(esym,E);
    for (int s=0; s < nseqs; s++) {
        Object[] x = xs[s];
        for (int i=0; i < x.length; i++) for (int k=1; k < x.length ; k++) E[mm.getSym(x[i])][mm.getSym(x[k])] += 1.0;
    }
    double count = 0;
    double countZero = 0;
    for (int k=0; k<nesym; k++) {
        double Eksum = 0;
        for (int b=0; b<nesym; b++) {
		count++;
		if ( E[k][b] == 0.0 ) countZero++;
		Eksum += E[k][b];
	}
        for (int b=0; b<nesym; b++) emat[k][b] = ( E[k][b] + 1.0 ) / ( Eksum + 0.0 + nesym);
    }
    if ( debugTrain ) System.err.println("Model sparsness : " + countZero + " in " + count + " ( " + ( countZero / count ) + " % )");
    return new MM(esym,emat);
  }

}
